package io.awen.modules.sign.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import io.awen.common.exception.RRException;
import io.awen.common.utils.DateUtils;
import io.awen.common.utils.PageUtils;
import io.awen.common.utils.Query;
import io.awen.common.utils.ShiroUtils;
import io.awen.config.MqttConfig;
import io.awen.modules.sign.StatusConstants;
import io.awen.modules.sign.dao.SignTaskDao;
import io.awen.modules.sign.entity.*;
import io.awen.modules.sign.qo.SignTaskQo;
import io.awen.modules.sign.service.*;
import io.awen.modules.sys.entity.SysUserEntity;
import io.awen.modules.sys.service.SysUserService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;


@Service("signTaskService")
public class SignTaskServiceImpl extends ServiceImpl<SignTaskDao, SignTaskEntity> implements SignTaskService {

    @Autowired
    private SignCarDispatchService signCarDispatchService;

    @Autowired
    private SignTaskService signTaskService;
    @Autowired
    @Lazy
    private SignCarService signCarService;
    @Autowired
    private SignContainerService signContainerService;

    @Autowired
    private SignTaskOperationService signTaskOperationService;

    @Autowired
    private SignSinkService signSinkService;

    @Autowired
    private SignCarWeightChangeService signCarWeightChangeService;
    @Autowired
    private SignUnitService signUnitService;

    @Autowired
    private SignTaskDao signTaskDao;

    @Autowired
    private MqttConfig mqttConfig;

    @Autowired
    private SysUserService sysUserService;

    private final static Gson gson = new Gson();


//    @Override
//    public PageUtils queryPage(Map<String, Object> params) {
//        IPage<SignTaskEntity> page = this.page(
//                new Query<SignTaskEntity>().getPage(params),
//                new QueryWrapper<SignTaskEntity>()
//        );
//
////        for (SignTaskEntity entity : page.getRecords()) {
////            SignContainerEntity container = signContainerService.findContainer(entity.getProductionContainerId());
////            entity.setContainer(container);
////            SignCarEntity car = signCarService.findCar(entity.getDispatchCarId());
////            entity.setCar(car);
////            SignUnitEntity unit = signUnitService.getById(entity.getCtUnitId());
////            entity.setUnit(unit);
////        }
//
//        return new PageUtils(page);
//    }

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<SignTaskEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("status", 3);
        wrapper.groupBy("id");
        wrapper.orderByDesc("create_time");

        IPage<SignTaskEntity> page = this.page(
                new Query<SignTaskEntity>().getPage(params),
                wrapper
        );

        List<SignTaskEntity> records = page.getRecords();
        List<Map<String, Object>> resultList = new ArrayList<>();

        for (SignTaskEntity record : records) {
            Map<String, Object> row = new HashMap<>();
            row.put("task_id", record.getId());
            row.put("task_name", record.getName());
            row.put("distance", record.getDistance());

            QueryWrapper<SignTaskOperationEntity> operationWrapper = new QueryWrapper<>();
            operationWrapper.eq("task_id", record.getId());
            List<SignTaskOperationEntity> operations = signTaskOperationService.list(operationWrapper);

            List<String> containerIds = new ArrayList<>();
            List<String> containerNames = new ArrayList<>();
            List<String> sheqvs = new ArrayList<>();
            List<String> sinkNames = new ArrayList<>();
            List<String> periods = new ArrayList<>();

            for (SignTaskOperationEntity operation : operations) {
                if (operation.getSinkId() == null) {
                    SignContainerEntity container = signContainerService.getById(operation.getContainerId());
                    containerIds.add(container.getId().toString());
                    containerNames.add(container.getName());
                    sheqvs.add(container.getSheqv());
                    periods.add(container.getPeriod());
                } else {
                    SignSinkEntity sink = signSinkService.getById(operation.getSinkId());
                    sinkNames.add(sink.getName());
                }

                if (operation.getChangeId() != null) {
                    SignCarWeightChangeEntity change = signCarWeightChangeService.getById(operation.getChangeId());
                    if (change != null && change.getStartWeight() != null) {
                        row.put("start_weight", change.getStartWeight());
                    }
                }
            }

            row.put("container_ids", String.join(",", containerIds));
            row.put("container_names", String.join(",", containerNames));
            row.put("sheqv", String.join(",", sheqvs));
            row.put("sink_names", String.join(",", sinkNames));
            row.put("period", String.join(",", periods));
            row.put("create_time", record.getCreateTime());

            QueryWrapper<SignCarWeightChangeEntity> changeWrapper = new QueryWrapper<>();
            changeWrapper.in("id", operations.stream().map(SignTaskOperationEntity::getChangeId).collect(Collectors.toList()));
            changeWrapper.select("MIN(create_time) AS start_time", "MAX(update_time) AS end_time");
            Map<String, Object> changeResult = signCarWeightChangeService.getMap(changeWrapper);
            row.put("start_time", changeResult.get("start_time"));
            row.put("end_time", changeResult.get("end_time"));

            resultList.add(row);
        }

        return new PageUtils(resultList, (int) page.getTotal(), (int) page.getSize(), (int) page.getCurrent());
//        return new PageUtils(page);
    }

    @Override
    public PageUtils listByQo(SignTaskQo qo) {
        IPage<SignTaskEntity> page = this.page(
                new Query<SignTaskEntity>().getPage(qo),
                new QueryWrapper<SignTaskEntity>().lambda()
                        .eq(qo.getCtUnitId() != null, SignTaskEntity::getCtUnitId, qo.getCtUnitId())
                        .ge(qo.getStartTime() != null, SignTaskEntity::getStartTime, qo.getStartTime())
                        .le(qo.getEndTime() != null, SignTaskEntity::getStartTime, qo.getEndTime())
                        .eq(StringUtils.isNotBlank(qo.getStatus()), SignTaskEntity::getStatus, qo.getStatus())
        );

        for (SignTaskEntity entity : page.getRecords()) {
            SignContainerEntity container = signContainerService.findContainer(entity.getProductionContainerId());
            entity.setContainer(container);
            SignCarEntity car = signCarService.getById(entity.getDispatchCarId());
            if (car != null) {
                SysUserEntity user = sysUserService.getById(car.getDriverId());
                car.setDriver(user);
            }
            entity.setCar(car);
            SignUnitEntity unit = signUnitService.getById(entity.getCtUnitId());
            entity.setUnit(unit);
        }

        return new PageUtils(page);
    }

    @Override
    public PageUtils queryPageWeightTotalGroupByCar(SignTaskQo qo) {

        List<Map<String, Object>> list = this.weightTotalGroupByCar(qo);

        IPage<Map<String, Object>> page = new Query<Map<String, Object>>().getPage(qo);
        //当前页
        long current = page.getCurrent();
        //每页数据条数
        long size = page.getSize();
        int count = list.size();
        List<Map<String, Object>> pageList = new ArrayList<>();
        //计算当前页第一条数据的下标
        long currId = current > 1 ? (current - 1) * size : 0;
        for (int i = 0; i < size && i < count - currId; i++) {
            pageList.add(list.get((int) currId + i));
        }
        page.setSize(size);
        page.setCurrent(current);
        page.setTotal(count);
        //计算分页总页数
        page.setPages(count % 10 == 0 ? count / 10 : count / 10 + 1);
        page.setRecords(pageList);

        return new PageUtils(page);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void autoTask(SignContainerEntity container, SignCarDispatchEntity dispatch) {
        // 寻找最近的车，如果没有则直接返回就行了
        SignCarEntity nearestCar = signCarService.findNearestCar(container);
        if (nearestCar == null) {
            return;
        }

        SignTaskEntity task = new SignTaskEntity();
        task.setDispatchId(dispatch.getId());
        task.setCtUnitId(dispatch.getCtUnitId());
        task.setDispatchCarId(nearestCar.getId());
        task.setProductionContainerId(container.getId());
        task.setStartTime(new Date());
        task.setStatus(StatusConstants.TASK_STATUS_WAITED);
        task.setName(container.getName() + "-收运任务[" + DateUtils.format(task.getStartTime(), "yyyy年MM月dd日HH时mm分") + "]");
        save(task);

        dispatch.setCurTaskId(task.getId());
        dispatch.setStatus(task.getStatus());
        dispatch.setResidueCalCount(dispatch.getResidueCalCount() - 1);
        signCarDispatchService.updateById(dispatch);

        // 通知司机
        mqttConfig.getMqttPushClient().publish(1, false, "task_" + nearestCar.getDriverId(), gson.toJson(task));
    }

    @Override
    public void autoTask(SignCarDispatchEntity dispatch) {
        SignContainerEntity container = signContainerService.getById(dispatch.getProductContainerId());
        autoTask(container, dispatch);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void manualTask(SignTaskEntity task) {
        if (task.getDispatchCarId() == null) {
            throw new RRException("请选择收运车辆");
        }

        if (task.getDispatchId() == null) {
            throw new RRException("请选择任务调度");
        }

        SignCarDispatchEntity dispatch = signCarDispatchService.getById(task.getDispatchId());
        if (dispatch == null) {
            throw new RRException("没有该任务调度");
        }

        if (dispatch.getCurTaskId() == null || StatusConstants.TASK_STATUS_REJECT.equals(dispatch.getStatus())) {
            task.setDispatchId(dispatch.getId());
            task.setCtUnitId(dispatch.getCtUnitId());
            task.setProductionContainerId(dispatch.getProductContainerId());
            task.setStartTime(new Date());
            task.setStatus(StatusConstants.TASK_STATUS_WAITED);

            SignContainerEntity container = signContainerService.findContainer(dispatch.getProductContainerId());
            task.setName(container.getName() + "-收运任务[" + DateUtils.format(task.getStartTime(), "yyyy年MM月dd日HH时mm分") + "]");

            save(task);

            dispatch.setCurTaskId(task.getId());
            dispatch.setStatus(task.getStatus());
            signCarDispatchService.updateById(dispatch);

            // 通知司机
            SignCarEntity car = signCarService.findCar(task.getDispatchCarId());
            mqttConfig.getMqttPushClient().publish(1, false, "task_" + car.getDriverId(), gson.toJson(task));
        } else {
            throw new RRException("该任务调度已经分配收运任务了");
        }
    }


    @Override
    public PageUtils driverList(SignTaskQo qo) {
        if (StringUtils.isBlank(qo.getStatus())) {
            throw new RRException("重要参数不能为空：status");
        }

        List<SignCarEntity> carList = signCarService.list(
                new QueryWrapper<SignCarEntity>().lambda()
                        .eq(SignCarEntity::getDriverId, ShiroUtils.getUserId())
        );

        if (carList == null || carList.size() == 0) {
            return null;
        }

        List<Long> carIdList = new ArrayList<>(carList.size());
        for (SignCarEntity car : carList) {
            carIdList.add(car.getId());
        }

        IPage<SignTaskEntity> page = this.page(
                new Query<SignTaskEntity>().getPage(qo),
                new QueryWrapper<SignTaskEntity>().lambda()
                        .eq(SignTaskEntity::getStatus, qo.getStatus())
                        .in(SignTaskEntity::getDispatchCarId, carIdList)
                        .ge(qo.getStartTime() != null, SignTaskEntity::getCreateTime, qo.getStartTime())
                        .le(qo.getEndTime() != null, SignTaskEntity::getCreateTime, qo.getEndTime())
        );

        for (SignTaskEntity task : page.getRecords()) {
            task.setContainer(signContainerService.findContainer(task.getProductionContainerId()));
            task.setDispatch(signCarDispatchService.getById(task.getDispatchId()));
        }
        return new PageUtils(page);
    }

    @Override
    public List<Map<String, Object>> weightTotalGroupByTimeLength(SignTaskQo qo) {
        List<Map<String, Object>> mapList = this.listMaps(
                new QueryWrapper<SignTaskEntity>()
                        .select("SUBSTR(end_time, 1, " + qo.getTimeLength() + ") data_time, sum(sy_weight) as syWeight, sum(TIMESTAMPDIFF(second, start_time, end_time)) as lostTime, count(id) taskCount")
                        .eq("status", "2")
                        .ge(qo.getStartTime() != null, "end_time", qo.getStartTime())
                        .le(qo.getEndTime() != null, "end_time", qo.getEndTime())
                        .groupBy("data_time")
                        .orderBy(StringUtils.isNotBlank(qo.getOrder()), "1".equals(qo.getOrder()), "data_time")
        );
        return mapList;
    }


    @Override
    public List<Map<String, Object>> weightTotalGroupByUnit(SignTaskQo qo) {
        List<Map<String, Object>> mapList = this.listMaps(
                new QueryWrapper<SignTaskEntity>()
                        .select("ct_unit_id as ctUnitId, sum(sy_weight) as syWeight, sum(TIMESTAMPDIFF(second, start_time, end_time)) as lostTime, count(id) taskCount")
                        .eq("status", "2")
                        .eq(qo.getCtUnitId() != null, "ct_unit_id", qo.getCtUnitId())
                        .ge(qo.getStartTime() != null, "end_time", qo.getStartTime())
                        .le(qo.getEndTime() != null, "end_time", qo.getEndTime())
                        .groupBy("ct_unit_id")
                        .orderBy(StringUtils.isNotBlank(qo.getOrder()), "1".equals(qo.getOrder()), "syWeight")
        );

        List<SignUnitEntity> unitList = signUnitService.list();
        Map<Long, SignUnitEntity> unitMap = new HashMap<>(unitList.size());
        for (SignUnitEntity entity : unitList) {
            unitMap.put(entity.getId(), entity);
        }

        for (Map<String, Object> map : mapList) {
            SignUnitEntity unit = unitMap.get(map.get("ctUnitId"));
            if (unit != null) {
                map.put("unitName", unit.getName());
            }
        }

        return mapList;
    }

    @Override
    public List<Map<String, Object>> weightTotalGroupByContainer(SignTaskQo qo) {
        List<Map<String, Object>> mapList = this.listMaps(
                new QueryWrapper<SignTaskEntity>()
                        .select("production_container_id as containerId, sum(sy_weight) as syWeight, sum(TIMESTAMPDIFF(second, start_time, end_time)) as lostTime, count(id) taskCount")
                        .eq("status", "2")
                        .eq(qo.getProductionContainerId() != null, "production_container_id", qo.getProductionContainerId())
                        .in(qo.getContainerIdList() != null && qo.getContainerIdList().size() > 0, "production_container_id", qo.getContainerIdList())
                        .ge(qo.getStartTime() != null, "end_time", qo.getStartTime())
                        .le(qo.getEndTime() != null, "end_time", qo.getEndTime())
                        .groupBy("production_container_id")
                        .orderBy(StringUtils.isNotBlank(qo.getOrder()), "1".equals(qo.getOrder()), "syWeight")
        );

        for (Map<String, Object> map : mapList) {
            SignContainerEntity container = signContainerService.findContainer((Long) map.get("containerId"));
            if (container != null) {
                map.put("containerName", container.getName());
            }
        }

        return mapList;
    }

    @Override
    public List<Map<String, Object>> weightTotalGroupByCar(SignTaskQo qo) {
        List<Map<String, Object>> mapList = this.listMaps(
                new QueryWrapper<SignTaskEntity>()
                        .select("dispatch_car_id as dispatchCarId, sum(sy_weight) as syWeight, sum(TIMESTAMPDIFF(second, start_time, end_time)) as lostTime, count(id) taskCount")
                        .eq("status", "2")
                        .eq(qo.getDispatchCarId() != null, "dispatch_car_id", qo.getDispatchCarId())
                        .in(qo.getCarIdList() != null && qo.getCarIdList().size() > 0, "dispatch_car_id", qo.getCarIdList())
                        .ge(qo.getStartTime() != null, "end_time", qo.getStartTime())
                        .le(qo.getEndTime() != null, "end_time", qo.getEndTime())
                        .groupBy("dispatch_car_id")
                        .orderBy(StringUtils.isNotBlank(qo.getOrder()), "1".equals(qo.getOrder()), "syWeight")
        );

        List<SignUnitEntity> unitList = signUnitService.list();
        Map<Long, SignUnitEntity> unitMap = new HashMap<>(unitList.size());
        for (SignUnitEntity entity : unitList) {
            unitMap.put(entity.getId(), entity);
        }

        for (Map<String, Object> map : mapList) {
            SignCarEntity car = signCarService.findCar((Long) map.get("dispatchCarId"));
            if (car != null) {
                map.put("numberPlate", car.getNumberPlate());
            }
        }


        return mapList;
    }

    @Override
    public List<Map<String, Object>> weightGroupByCarAndTime(SignTaskQo qo) {
        return baseMapper.weightGroupByCarAndTime(qo.getDispatchCarId(), qo.getStartTime(), qo.getEndTime(), qo.getTimeLength());
    }

    @Override
    public Map<String, Object> totalByCar(SignTaskQo qo) {
        return null;
    }

    @Override
    public List<Map<String, Object>> getDailyTotalReport(String startTime, String endTime) {
        return signTaskDao.getDailyTotalReport(startTime, endTime);
    }

    @Override
    public List<Map<String, Object>> getTotalByCar(String startTime, String endTime) {
        List<Map<String, Object>> list = signTaskDao.getTotalByCar(startTime, endTime);


        for (Map map : list) {

            Long carId = (Long) map.get("dispatchCarId");
            SignCarEntity car = signCarService.findCar(carId);
            if (car != null) {
                Long driverId = car.getDriverId();
                SysUserEntity user = sysUserService.getById(driverId);
                map.put("user", user);
            }

            map.put("car", car);
        }

        return list;
    }


}